package com.efivestar.idm.api.service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.efivestar.idm.api.common.Cons;
import com.efivestar.idm.api.domain.DeviceDomain;
import com.efivestar.idm.api.domain.EventDomain;
import com.efivestar.idm.api.domain.Lock2UserDomain;
import com.efivestar.idm.api.domain.LockRemoteControlDomain;
import com.efivestar.idm.api.domain.WeixinJoinDeviceDomain;
import com.efivestar.idm.api.exception.AppException;
import com.efivestar.idm.api.repository.CameraMapper;
import com.efivestar.idm.api.repository.DeviceMapper;
import com.efivestar.idm.api.repository.EventMapper;
import com.efivestar.idm.api.repository.Lock2UserMapper;
import com.efivestar.idm.api.repository.LockRemoteControlMapper;
import com.efivestar.idm.api.repository.WeixinJoinDeviceMapper;
import com.efivestar.idm.api.repository.WeixinUserMapper;
import com.efivestar.idm.api.rest.vm.DoorRemoteControlVM;
import com.efivestar.idm.api.service.dto.DeviceWithNameDto;
import com.efivestar.idm.api.service.dto.EventDto;
import com.efivestar.idm.api.service.dto.UserDto;
import com.efivestar.idm.api.util.weixin.ContextHolder;
import com.efivestar.idm.api.util.weixin.WeixinApi;
import com.github.pagehelper.PageInfo;

import tk.mybatis.mapper.entity.Example;

@Service
public class WeixinService {

    Logger logger = LoggerFactory.getLogger(WeixinService.class);

    @Autowired
    private WeixinJoinDeviceMapper weixinJoinDeviceMapper;

    @Autowired
    private WeixinUserMapper weixinUserMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private EventMapper eventMapper;

    @Autowired
    private Lock2UserMapper lock2UserMapper;

    @Autowired
    private CameraMapper cameraMapper;

    @Autowired
    private WeixinApi weixinApi;

    @Autowired
    private LockRemoteControlMapper lockRemoteControlMapper;


    @Value("${weixin.warningTemplateId:jZt8KiYA6wsIkVqNqL12GAfVnzZftFD895bEva0zPLA}")
    private String warningWeixinTemplateId;

    @Value("${service.domain:51ai.cc}")
    private String domain;
    /**
     * 绑定设备到用户.
     * 只允许绑定一个.
     * @param weixinJoinDeviceDomain
     * @return
     */
    @Transactional
    public boolean bindDeviceToUser(WeixinJoinDeviceDomain weixinJoinDeviceDomain){
        String openId = ContextHolder.getWeixinUser().getOpenId();
        weixinJoinDeviceDomain.setOpenId(openId);
        WeixinJoinDeviceDomain dbWeixinJoinDeviceDomain = weixinJoinDeviceMapper.selectByPrimaryKey(weixinJoinDeviceDomain);
        if (StringUtils.isNotEmpty(weixinJoinDeviceDomain.getDeviceId())) {
            //查询设备是否存在
            DeviceDomain deviceDomain = deviceMapper.selectByPrimaryKey(weixinJoinDeviceDomain.getDeviceId());
            if (deviceDomain == null) {
                throw new AppException("002", "设备不存在,请先对设备上电");
            }
        }
        //已经绑定过，此时是属于修改。
        if (dbWeixinJoinDeviceDomain != null) {
            //智能锁编号存在，但是再次绑定的时候，发现绑定的编号和数据库原有的编号不一样了。这是违反操作逻辑的，必须先解绑，再绑定
            if (StringUtils.isNotEmpty(dbWeixinJoinDeviceDomain.getDeviceId()) && !weixinJoinDeviceDomain.getDeviceId().equals(dbWeixinJoinDeviceDomain.getDeviceId())) {
                throw new AppException("003", "违法操作");
            }
            //摄像头编号存在，但是再次绑定的时候，发现绑定的编号和数据库原有的编号不一样了。这是违反操作逻辑的，必须先解绑，再绑定
            if (StringUtils.isNotEmpty(dbWeixinJoinDeviceDomain.getCameraId()) && !weixinJoinDeviceDomain.getCameraId().equals(dbWeixinJoinDeviceDomain.getCameraId())) {
                throw new AppException("003", "违法操作");
            }
            //数据库中智能锁不存在，并且此时绑定了智能锁， 所以此时必须校验密码是否存在
            if (StringUtils.isEmpty(dbWeixinJoinDeviceDomain.getDeviceId()) && StringUtils.isNotEmpty(weixinJoinDeviceDomain.getDeviceId())){
                if (StringUtils.isEmpty(weixinJoinDeviceDomain.getPassword())) {
                    throw new AppException("004", "用户密码不允许为空");
                } else {
                    //设置用户远程开锁的密码
                    dbWeixinJoinDeviceDomain.setPassword(getUserRemoteControlLockMd5Password(weixinJoinDeviceDomain.getPassword()));
                }
            }
            dbWeixinJoinDeviceDomain.setCameraId(weixinJoinDeviceDomain.getCameraId());
            dbWeixinJoinDeviceDomain.setDeviceId(weixinJoinDeviceDomain.getDeviceId());
            dbWeixinJoinDeviceDomain.setName(weixinJoinDeviceDomain.getName());
            weixinJoinDeviceMapper.updateByPrimaryKey(dbWeixinJoinDeviceDomain);
        } else {
            //未绑定过，此时属于新增
            if (StringUtils.isEmpty(weixinJoinDeviceDomain.getPassword())) {
                throw new AppException("004", "用户密码不允许为空");
            } else {
                //设置用户远程开锁的密码
                weixinJoinDeviceDomain.setPassword(getUserRemoteControlLockMd5Password(weixinJoinDeviceDomain.getPassword()));
            }
            weixinJoinDeviceDomain.setCreateTime(new Date());
            weixinJoinDeviceMapper.insert(weixinJoinDeviceDomain);
        }
        return true;
    }



    /**
     * 删除用户和设备的绑定关系.
     * @param openId
     * @param deviceId
     */
    public void deleteUserDevice(String openId, String deviceId) {
        weixinJoinDeviceMapper.deleteByExample(generateUserJoinDeviceExample(openId, deviceId));
    }

    /**
     * 生成设备和用户绑定关系拼接条件
     * @param openId
     * @param deviceId
     * @return
     */
    private Example generateUserJoinDeviceExample(String openId, String deviceId) {
        Example example = new Example(WeixinJoinDeviceDomain.class);
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotEmpty(openId)){
            criteria.andEqualTo("openId", openId);
        }
        if (StringUtils.isNotEmpty(deviceId)){
            criteria.andEqualTo("deviceId", deviceId);
        }
        return example;
    }

    /**
     * 解绑用户的设备.
     */
    public void unBindDeviceToUser() {
        String openId = ContextHolder.getWeixinUser().getOpenId();
        weixinJoinDeviceMapper.deleteByExample(generateUserJoinDeviceExample(openId, null));
    }

    /**
     * 解绑设备.
     * @param weixinJoinDeviceDomain
     * @param type
     */
    public void unBindDevice(WeixinJoinDeviceDomain weixinJoinDeviceDomain, int type) {
        String openId = ContextHolder.getWeixinUser().getOpenId();
        WeixinJoinDeviceDomain weixinJoinDevice  = weixinJoinDeviceMapper.selectByPrimaryKey(openId);
        if (type == Cons.LOCK) {
            if (!weixinJoinDevice.getPassword().equals(getUserRemoteControlLockMd5Password(weixinJoinDeviceDomain.getPassword()))) {
                throw new AppException("001", "密码不正确");
            }
            if (StringUtils.isNotEmpty(weixinJoinDeviceDomain.getDeviceId()) && weixinJoinDeviceDomain.getDeviceId().equals(weixinJoinDevice.getDeviceId())) {
                if (StringUtils.isEmpty(weixinJoinDevice.getCameraId())) {//摄像头id和指纹锁id都不存在了，此时删除绑定关系
                    weixinJoinDeviceMapper.deleteByPrimaryKey(weixinJoinDevice);
                } else {
                    weixinJoinDevice.setDeviceId(null);
                    weixinJoinDevice.setPassword(null);
                    weixinJoinDeviceMapper.updateByPrimaryKey(weixinJoinDevice);
                }
            }
        } else if (type == Cons.CAMERA) {
            if (StringUtils.isNotEmpty(weixinJoinDeviceDomain.getCameraId()) && weixinJoinDeviceDomain.getCameraId().equals(weixinJoinDevice.getCameraId())) {
                if (StringUtils.isEmpty(weixinJoinDevice.getDeviceId())) {//摄像头id和指纹锁id都不存在了，此时删除绑定关系
                    weixinJoinDeviceMapper.deleteByPrimaryKey(weixinJoinDevice);
                } else {
                    weixinJoinDevice.setCameraId(null);
                    weixinJoinDeviceMapper.updateByPrimaryKey(weixinJoinDevice);
                }
            }
        }

//        if (StringUtils.isEmpty(weixinJoinDevice.getDeviceId()) && StringUtils.isEmpty(weixinJoinDevice.getCameraId())) {
//            weixinJoinDeviceMapper.deleteByExample(generateUserJoinDeviceExample(openId, null));
//        }
    }


    /**
     * 分页查询事件.
     * @param eventDto
     * @param pageInfo
     * @return
     */
    public PageInfo selectEventByPage(EventDto eventDto, PageInfo pageInfo) {
        pageInfo.setStartRow((pageInfo.getPageNum() -1) * pageInfo.getPageSize() );
        Map<String, Object> eventMap = new HashMap<>();
        eventMap.put("event", eventDto);
        eventMap.put("page", pageInfo);
        List<EventDomain> eventDomains = eventMapper.selectEventsByPage(eventMap);
        int eventCount = eventMapper.selectEventCount(eventMap);
        pageInfo.setTotal(eventCount);
        pageInfo.setList(eventDomains);
        pageInfo.setSize(eventDomains.size());
        return pageInfo;
    }


    public List<Lock2UserDomain>  queryLockUsers() {
        DeviceWithNameDto mydevice = getMydevice();
        if (mydevice != null) {
            String lockId = mydevice.getDeviceId();
            List<Lock2UserDomain> lock2UserDomains = lock2UserMapper.selectByExample(lock2UserWhereCause(lockId));
            return lock2UserDomains;
        }
        return null;
    }


    /**
     * 获取我的设备.
     * @return
     */
    public DeviceWithNameDto getMydevice(){
        String openId = ContextHolder.getWeixinUser().getOpenId();
        List<DeviceWithNameDto> devices = weixinUserMapper.getDevicesByOpenId(openId);
        DeviceWithNameDto deviceWithNameDto = null;
        if (devices != null && !devices.isEmpty()) {
            deviceWithNameDto = devices.get(0);
        }
        return deviceWithNameDto;
    }


    /**
     * 用户条件查询.
     * @param lockId
     * @return
     */
    private Example lock2UserWhereCause(String lockId) {
        Example example = new Example(Lock2UserDomain.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("lockId", lockId);
        return example;
    }


    /**
     * 更新用户的名称和劫持报警状态.
     * @param userDtos
     */
    public void updateUserNameAndHijackStatus(List<UserDto> userDtos) {
        List<Lock2UserDomain> lock2UserDomains = queryLockUsers();
        for (Lock2UserDomain lock2UserDomain : lock2UserDomains) {
            setUserNameAndDefenseStatus(lock2UserDomain, userDtos);
        }
        for (Lock2UserDomain lock2UserDomain : lock2UserDomains) {
           lock2UserMapper.updateByPrimaryKey(lock2UserDomain);
        }
    }

    private void setUserNameAndDefenseStatus(Lock2UserDomain lock2UserDomain, List<UserDto> userDtos) {
        int type = lock2UserDomain.getType().intValue();
        String userCode = lock2UserDomain.getUserCode();
        if (userDtos != null && !userCode.isEmpty()) {
            List<UserDto> selectedUser = userDtos.stream().filter(d -> (d.getId() == lock2UserDomain.getId())).collect(Collectors.toList());
            if (!selectedUser.isEmpty()) {
                UserDto userDto = selectedUser.get(0);
                lock2UserDomain.setHijackStatus(userDto.isHijackStatus());
                lock2UserDomain.setUserName(userDto.getUserName());
            }
        }
    }

    /**
     * 根据锁id查询该锁对应的用户.
     * @param lockId
     * @return
     */
    public List<Lock2UserDomain> queryLockUsersByLockId(String lockId) {
        List<Lock2UserDomain> lock2UserDomains = lock2UserMapper.selectByExample(lock2UserWhereCause(lockId));
        return lock2UserDomains;
    }


//   ********* 远程开锁相关 *******************
    /**
     * 获取当前智能锁的远程开锁状态.
     * @param lockId
     * @return
     */
    public LockRemoteControlDomain getLockRemoteControl(String lockId){
        LockRemoteControlDomain lockRemoteControlDomain = lockRemoteControlMapper.selectByPrimaryKey(lockId);
        return lockRemoteControlDomain;
    }



    /**
     * 获取我的智能锁的状态.
     * @return
     */
    public LockRemoteControlDomain getMyLockRemoteControl(){
        DeviceWithNameDto mydevice = getMydevice();
        String lockId = mydevice.getDeviceId();
        LockRemoteControlDomain lockRemoteControlDomain = lockRemoteControlMapper.selectByPrimaryKey(lockId);
        return lockRemoteControlDomain;
    }


    /**
     * 智能锁请求远程开锁.
     * @param lockId
     */
    public void requestLockRemoteControl(String lockId) {
        LockRemoteControlDomain myLockRemoteControl = getLockRemoteControl(lockId);
        if (myLockRemoteControl != null) {
            myLockRemoteControl.setOperated(false);
            myLockRemoteControl.setRequestTime(new Date());
            lockRemoteControlMapper.updateByPrimaryKey(myLockRemoteControl);
        } else {
            LockRemoteControlDomain lockRemoteControlDomain = LockRemoteControlDomain.builder().lockId(lockId).allowed(false).operated(false).requestTime(new Date()).build();
            lockRemoteControlMapper.insert(lockRemoteControlDomain);
        }
    }


    /**
     * 用户远程操作开锁.
     * 001 密码不正确
     * 002 远程开锁已操作
     * 003 操作超时
     * 101 用户尚未设置开锁密码.
     * @param doorRemoteControlVM
     */
    public void remoteControlOpDoor(DoorRemoteControlVM doorRemoteControlVM){
        String openId = ContextHolder.getWeixinUser().getOpenId();
        String password = doorRemoteControlVM.getPassword();
        //true 代表允许开锁 false 代表拒绝开锁
        boolean isAllowed = doorRemoteControlVM.isAllowed();
        DeviceWithNameDto mydevice = getMydevice();
        if (mydevice != null && StringUtils.isNotEmpty(mydevice.getDeviceId())) {
            String lockId = mydevice.getDeviceId();
            if (checkOpRemoteDoor(lockId, openId, password)) {
                opLockRemoteControl(lockId, isAllowed);
            }
        }
        
        // 将用户的操作结果以命令的形式发送到电信IOT平台
    	try {
    		String iotDeviceId = mydevice.getIotDeviceId();
    		if (StringUtils.isNotBlank(iotDeviceId)) {// 如果 iotDeviceId 不为空，则发送命令
    			String answerCode = null;// 应答码
        		if (isAllowed) {
        			answerCode = "02";// 0x02：允许开锁
        		} else {
        			answerCode = "03";// 0x03：拒绝开锁
        		}
        		String hex = Cons.UDP_HEAD_EF01 + "FFFFFFFF070008" + Cons.UDP_COMMAND_REMOTE_DOOR_CONOTROL_STATUS + answerCode + String.format("%08x", 18);
    			IotService.postAsynCommandV4(iotDeviceId, "REMOTE_UNLOCK", "{\"remote_unlock_status\":\"" + hex + "\"}");
    		}
		} catch (Exception e) {
			e.printStackTrace();
		}
    }

    /**
     * 检查用户对该智能锁是否允许操作.
     * 1：检查密码是否匹配。
     * 2：检查当前智能锁的操作状态（当为未操作的时候才允许继续操作）
     * 3：检查智能锁发起远程开锁请求到现在的时间，（5分钟内有效）
     * @param lockId
     * @param openId
     * @param password
     */
    public boolean checkOpRemoteDoor(String lockId, String openId, String password) {
        WeixinJoinDeviceDomain userLockPasswordDomain = getWeixinJoinDeviceDomain(openId);
        //判断用户是否有该智能锁的操作权限
        if (userLockPasswordDomain != null ) {
//            判断用户是否设置了智能锁密码
            if (StringUtils.isEmpty(userLockPasswordDomain.getPassword())) {
                throw new AppException("101", "用户尚未设置开锁密码.");
            }
            //校验密码
            if(!userLockPasswordDomain.getPassword().equals(getUserRemoteControlLockMd5Password(password))) {
                throw new AppException("001", "密码不正确");
            }
            LockRemoteControlDomain lockRemoteControl = getLockRemoteControl(lockId);
            // 校验智能锁的操作状态
            if (lockRemoteControl.isOperated()) {
                throw new AppException("002", "远程开锁已操作");
            }
            // 校验请求远程开锁的时间是否和当前时间匹配
            Date now = new Date();
            long interval = now.getTime() - lockRemoteControl.getRequestTime().getTime();
            // 此处设置6分钟，因为用户反映要一段时间
            if (interval > 360000) {
                throw new AppException("003", "操作超时");
            } else if (interval < 0) {
                logger.error("lockId:{}, openId:{} 非法操作时间,请求开锁时间在执行开锁时间之后，不符合逻辑", new Object[]{lockId, openId});
                throw new AppException("004", "非法操作时间");
            }
        } else {
            logger.error("lockId:{}, openId:{} 尚未绑定开锁密码", new Object[]{lockId, openId});
            throw new AppException("102", "用户和开锁密码未绑定");
        }
        return true;
    }


    private String getUserRemoteControlLockMd5Password(String password) {
        return DigestUtils.md5Hex(password + Cons.USER_LOCK_REMOTE_CONTROL_PASSWORD_SALT);
    }



    /**
     * 操作远程开锁的锁状态.
     * @param lockId
     * @param isAllowed true 代表允许开锁 false 代表拒绝开锁.
     */
    public void opLockRemoteControl(String lockId, boolean isAllowed) {
        LockRemoteControlDomain myLockRemoteControl = getLockRemoteControl(lockId);
        if (myLockRemoteControl != null) {
            myLockRemoteControl.setOperated(true);
            myLockRemoteControl.setAllowed(isAllowed);
            myLockRemoteControl.setRequestTime(new Date());
            //开锁的序列号循环递增.
            if (myLockRemoteControl.getSequence() == Integer.MAX_VALUE) {
                myLockRemoteControl.setSequence(0);
            } else {
                myLockRemoteControl.setSequence(myLockRemoteControl.getSequence() + 1);
            }
            lockRemoteControlMapper.updateByExample(myLockRemoteControl, updateRemoteLockOpStatusWhereCause(lockId));
        }
    }


    /**
     * 更新智能锁远程开锁的where条件。
     * 必须是未操作的，即：operated 为false的.
     * @return
     */
    private Example updateRemoteLockOpStatusWhereCause(String lockId){
        Example example = new Example(LockRemoteControlDomain.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("lockId", lockId);
        criteria.andEqualTo("operated", false);
        return example;
    }


//   *********用户远程开锁密码相关***********

    /**
     * 重置远程开锁密码.
     * @param newPassword
     */
    public void reSetRemoteOpenDoorPassword(String newPassword) {
        DeviceWithNameDto mydevice = getMydevice();
        if (mydevice != null) {
            EventDomain latestOpenDoorEvent = getLatestOpenDoorEvent();
            if (latestOpenDoorEvent != null) {
                Date createTime = latestOpenDoorEvent.getCreateTime();
                Date now = new Date();
                //当前时间和最新的一次开门事件间隔小于5分钟，此时可以重置密码.
                if (now.getTime() - createTime.getTime() < 300000) {
                    WeixinJoinDeviceDomain weixinJoinDeviceDomain = weixinJoinDeviceMapper.selectByPrimaryKey(mydevice.getOpenId());
                    weixinJoinDeviceDomain.setPassword(getUserRemoteControlLockMd5Password(newPassword));
                    weixinJoinDeviceMapper.updateByPrimaryKey(weixinJoinDeviceDomain);
                }
            }
        }
    }


    /**
     * 获取最新一次的开门事件.
     */
    public EventDomain getLatestOpenDoorEvent() {
        PageInfo pageInfo = new PageInfo();
        pageInfo.setPageSize(1);
        pageInfo.setPageNum(1);
        pageInfo.setStartRow((pageInfo.getPageNum() -1) * pageInfo.getPageSize() );
        EventDto eventDto = new EventDto();
        DeviceWithNameDto mydevice = getMydevice();
        if (mydevice != null) {
            eventDto.setDeviceId(mydevice.getDeviceId());
            eventDto.setEventType(Cons.OPEN_DOOR_EVENT_TYPE);
            Map<String, Object> eventMap = new HashMap<>();
            eventMap.put("event", eventDto);
            eventMap.put("page", pageInfo);
            List<EventDomain> eventDomains = eventMapper.selectEventsByPage(eventMap);
            if (eventDomains != null && !eventDomains.isEmpty()) {
                EventDomain eventDomain = eventDomains.get(0);
                return eventDomain;
            }
        }
        return null;
    }



    /**
     * 修改远程开锁密码.
     * @param oldPassword
     * @param newPassword
     */
    public void modifyRemoteOpenDoorPassword(String oldPassword, String newPassword) {
        DeviceWithNameDto mydevice = getMydevice();
        if (mydevice != null) {
            WeixinJoinDeviceDomain weixinJoinDeviceDomain = getWeixinJoinDeviceDomain(mydevice.getOpenId());
            //老密码匹配.
            if (weixinJoinDeviceDomain.getPassword().equalsIgnoreCase(getUserRemoteControlLockMd5Password(oldPassword))) {
                weixinJoinDeviceDomain.setPassword(getUserRemoteControlLockMd5Password(newPassword));
                weixinJoinDeviceMapper.updateByPrimaryKey(weixinJoinDeviceDomain);
            } else {
                throw new AppException("001", "密码不正确");
            }
        }
    }

    /**
     * 获取用户的远程开锁密码对象.
     * @return
     */
    private WeixinJoinDeviceDomain getWeixinJoinDeviceDomain(String openId) {
        WeixinJoinDeviceDomain dbWeixinJoinDeviceDomain = weixinJoinDeviceMapper.selectByPrimaryKey(openId);
        return dbWeixinJoinDeviceDomain;
    }









}



